/*
 * @Author: liuzelin
 * @Date: 2024-04-06 13:42:19
 * @LastEditors: liuzelin
 * @LastEditTime: 2024-05-03 19:49:44
 * @Description: file content
 */
#pragma once

#include <limits>
#include <queue>
#include "panda-framework/panda/panda.hpp"

namespace PandaUtils {
template <typename T>
class SeqSortWarper {
 public:
  bool operator()(const T &a, const T &b) const {
    if (a == b) return false;
    T diff = a > b ? a - b : b - a;
    return diff < (std::numeric_limits<T>::max() / 2);
  }
};

template <typename T>
class SeqStatistician {
 public:
  SeqStatistician() = default;
  void UpdateSeq(T seq) {
    if (num == 0) {
      max_seq = seq;
      min_seq = seq;
    } else {
      if (SeqSortWarper<T>()(seq, *max_seq)) {
        max_seq = seq;
      }
      if (SeqSortWarper<T>()(*min_seq, seq)) {
        min_seq = seq;
      }
    }
    num++;
  }
  void Clear() {
    num = 0;
    min_seq = std::nullopt;
    max_seq = std::nullopt;
  }
  float GetLossRate() {
    if (num != 0) {
      T should = *max_seq - *min_seq + 1;
      return (should - num) * 100.f / should;
    } else {
      return 0;
    }
  }

 private:
  std::optional<T> max_seq;
  std::optional<T> min_seq;
  size_t num = 0;
};

class NetworkSpeedStatistics {
 public:
  NetworkSpeedStatistics(uint64_t duration = 100):duration(duration){}
  void Update(size_t size, uint64_t now = Panda::Time::NowMs()) {
    sum += size;
    infos.emplace(now, size);
    InnerCheck(now);
  }

  uint64_t GetSpeed(uint64_t now = Panda::Time::NowMs()) {
    InnerCheck(now);
    if (infos.empty()) return 0;
    uint64_t diff_ms = infos.back().time - infos.front().time;
    if (diff_ms) {
      return sum * 1000 / diff_ms;
    }
    return sum * 10;
  }

 private:
  void InnerCheck(uint64_t now) {
    while (infos.empty() == false) {
      if (now > infos.front().time + duration) {
        PandaCheck(sum >= infos.front().size)
            << "NetworkSpeedStatistics Sum Can`t Less Cache:" << sum
            << " Time:" << infos.front().time << " Size:" << infos.front().size;
        sum -= infos.front().size;
        infos.pop();
        continue;
      }
      break;
      /* code */
    }
  }
  struct Node {
    explicit Node(uint64_t t, size_t s) : size(s), time(t) {}
    uint64_t time;
    size_t size;
  };
  uint64_t duration = 100;
  std::queue<Node> infos;
  size_t sum = 0;
};
}  // namespace PandaUtils